import * as THREE from "three"
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { vertex, fragment, override_vertex, override_fragment } from "./depthRainShader.js";

/**@type {THREE.WebGLRenderer} */
let renderer
let camera, scene, controls, stats
let target

let rain
let material

const WIDTH = 256
const HEIGHT = 256




let time = 0

let plane, box, depthScene, orthCamera;

const clock = new THREE.Clock();

init();
animate();

function init() {
    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1500);
    camera.position.set(0, 200, 600);

    scene = new THREE.Scene();
    scene.background = new THREE.Color(0x333333);

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    box = new THREE.Box3(
        new THREE.Vector3(-200, 0, -200),
        new THREE.Vector3(200, 200, 200)
    );

    rain = createRain();

    plane = createPlane();

    createDepth();

    var axesHelper = new THREE.AxesHelper(5000);
    scene.add(axesHelper);

    stats = new Stats();
    document.body.appendChild(stats.dom);

    controls = new OrbitControls(camera, renderer.domElement);

    window.addEventListener("resize", onWindowResize, false);
}

//创建深度图
function createDepth() {
    target = new THREE.WebGLRenderTarget(WIDTH, HEIGHT);
    target.texture.format = THREE.RGBFormat;
    target.texture.minFilter = THREE.NearestFilter;
    target.texture.magFilter = THREE.NearestFilter;
    target.texture.generateMipmaps = false;

    orthCamera = new THREE.OrthographicCamera();
    const center = new THREE.Vector3();
    box.getCenter(center);

    orthCamera.left = box.min.x - center.x;
    orthCamera.right = box.max.x - center.x;
    orthCamera.top = box.max.z - center.z;
    orthCamera.bottom = box.min.z - center.z;
    orthCamera.near = .1;
    orthCamera.far = box.max.y - box.min.y;

    orthCamera.position.copy(center);
    orthCamera.position.y += box.max.y - center.y;
    orthCamera.lookAt(center);

    orthCamera.updateProjectionMatrix();
    orthCamera.updateWorldMatrix()

    const helper = new THREE.CameraHelper(orthCamera)
    scene.add(helper);

    depthScene = new THREE.Scene();
    depthScene.overrideMaterial = new THREE.ShaderMaterial({
        vertexShader: override_vertex,
        fragmentShader: override_fragment,
    });

    renderer.setRenderTarget(target);
    depthScene.children = [plane];
    renderer.render(depthScene, orthCamera);
    renderer.setRenderTarget(null);
}

//创建雨
function createRain() {

    //创建雨
    material = new THREE.MeshBasicMaterial({
        transparent: true,
        opacity: 0.8,
        depthWrite: false,
    });

    material.onBeforeCompile = function (shader, renderer) {

        shader.vertexShader = vertex
        shader.fragmentShader = fragment

        shader.uniforms.cameraPosition = {
            value: new THREE.Vector3(0, 200, 0)
        }
        shader.uniforms.top = {
            value: box.max.y
        }
        shader.uniforms.bottom = {
            value: box.min.y
        }
        shader.uniforms.time = {
            value: 0
        }

        shader.uniforms.cameraMatrix = {
            value: new THREE.Matrix4()
        }
        shader.uniforms.tDepth = {
            value: target.texture
        }
        material.uniforms = shader.uniforms;
    };

    const geometry = new THREE.BufferGeometry();

    const vertices = [];
    const poses = [];
    const uvs = [];
    const indices = [];

    for (let i = 0; i < 6000; i++) {
        const pos = new THREE.Vector3();
        pos.x = Math.random() * (box.max.x - box.min.x) + box.min.x;
        pos.y = Math.random() * (box.max.y - box.min.y) + box.min.y;
        pos.z = Math.random() * (box.max.z - box.min.z) + box.min.z;

        const height = (box.max.y - box.min.y) / 15;
        const width = height / 50;

        vertices.push(
            pos.x + width,
            pos.y + height,
            pos.z,
            pos.x - width,
            pos.y + height,
            pos.z,
            pos.x - width,
            pos.y,
            pos.z,
            pos.x + width,
            pos.y,
            pos.z
        );

        poses.push(
            pos.x,
            pos.y,
            pos.z,
            pos.x,
            pos.y,
            pos.z,
            pos.x,
            pos.y,
            pos.z,
            pos.x,
            pos.y,
            pos.z
        );

        uvs.push(1, 1, 0, 1, 0, 0, 1, 0);

        indices.push(
            i * 4 + 0,
            i * 4 + 1,
            i * 4 + 2,
            i * 4 + 0,
            i * 4 + 2,
            i * 4 + 3
        );
    }

    geometry.setAttribute(
        "position",
        new THREE.BufferAttribute(new Float32Array(vertices), 3)
    );
    geometry.setAttribute(
        "pos",
        new THREE.BufferAttribute(new Float32Array(poses), 3)
    );
    geometry.setAttribute(
        "uv",
        new THREE.BufferAttribute(new Float32Array(uvs), 2)
    );
    geometry.setIndex(new THREE.BufferAttribute(new Uint32Array(indices), 1));

    var mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);

    return mesh;
}

//创建地形
function createPlane() {

    const geometry = new THREE.PlaneGeometry(100, 400)
    geometry.rotateX(-Math.PI / 2)

    const mesh = new THREE.Mesh(
        geometry,
        new THREE.MeshBasicMaterial({ side: THREE.DoubleSide })
    );
    mesh.position.y = 100
    scene.add(mesh);

    return mesh;
}


function onWindowResize() {
    windowHalfX = window.innerWidth / 2;
    windowHalfY = window.innerHeight / 2;

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
}

//
function animate() {
    requestAnimationFrame(animate);

    render();
    stats.update();
}

function render() {
    time = clock.getElapsedTime() / 2;

    if (material.uniforms) {
        material.uniforms.cameraPosition.value = camera.position;
        material.uniforms.time.value = time;

        material.uniforms.cameraMatrix.value = new THREE.Matrix4().multiplyMatrices(
            orthCamera.projectionMatrix,
            orthCamera.matrixWorldInverse
        );

        // renderer.setRenderTarget(target);
        // renderer.render(depthScene, orthCamera);
        // renderer.setRenderTarget(null);
        // material.uniforms.tDepth.value = target.texture;
    }

    renderer.render(scene, camera);

}